package com.ecarx.camera.worker.car.aosp;

import android.car.hardware.CarPropertyValue;

import com.ecarx.camera.utils.LogUtils;
import com.ecarx.camera.worker.car.FunctionStatus;
import com.ecarx.camera.worker.car.ICarFunctionProxy;
import com.ecarx.camera.worker.car.adaptapi.AdaptWrapperHelper;
import com.ecarx.camera.worker.car.constant.IllegalData;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 车身功能可用状态、设置值、取值或监听接口的Aosp实现
 */
public class CarFunctionProxyAospImpl implements ICarFunctionProxy {

    private static final String TAG = "CarFunctionProxyAospImpl";

    /**
     * 记录IFunctionValueWatcher与PropertyEventCallbackWrapper映射关系
     */
    private final ConcurrentHashMap<Integer, PropertyEventCallbackWrapper> mEventCallbackWrapperMap = new ConcurrentHashMap<>();

    @Override
    public boolean isFunctionSupported(int functionId) {
        int propId = AdaptWrapperHelper.functionIdToPropertyId(functionId);
        return AospPropertyManagerHelper.isCarPropertySupported(propId);
    }

    @Override
    public boolean isFunctionActive(int functionId) {
        return isFunctionActive(functionId, AospPropertyMapping.DEFAULT_ZONE);
    }

    @Override
    public boolean isFunctionActive(int functionId, int zone) {
        if (AdaptWrapperHelper.isNotNeedCheckActive(functionId)) {
            LogUtils.d(TAG, "isFunctionActive, functionId:" + functionId + ", no need check, return true.");
            return true;
        }
        int propId = AdaptWrapperHelper.functionIdToPropertyId(functionId);
        return AospPropertyManagerHelper.isCarPropertyActive(propId, zone);
    }

    @Override
    public boolean setFunctionValue(int functionId, int functionValue) {
        return setFunctionValue(functionId, AospPropertyMapping.DEFAULT_ZONE, functionValue);
    }

    @Override
    public boolean setFunctionValue(int functionId, int zone, int functionValue) {
        return AospPropertyManagerHelper.setCarIntProperty(AdaptWrapperHelper.functionIdToPropertyId(functionId), zone
                , AdaptWrapperHelper.functionValueToPropertyValue(functionId, functionValue));
    }

    @Override
    public int getFunctionValue(int functionId) {
        return getFunctionValue(functionId, AospPropertyMapping.DEFAULT_ZONE);
    }

    @Override
    public int getFunctionValue(int functionId, int zone) {
        final CarPropertyValue<Integer> value = AospPropertyManagerHelper.getCarPropertyValue(AdaptWrapperHelper.functionIdToPropertyId(functionId), zone);
        if (value == null) {
            return IllegalData.FunctionIllegalValue.NOT_AVAILABLE_RTN_VALUE;
        }
        return AdaptWrapperHelper.propertyValueToFunctionValue(functionId, value.getValue());
    }

    @Override
    public boolean setFunctionFloatValue(int functionId, float functionValue) {
        return setFunctionFloatValue(functionId, AospPropertyMapping.DEFAULT_ZONE, functionValue);
    }

    @Override
    public boolean setFunctionFloatValue(int functionId, int zone, float functionValue) {
        return AospPropertyManagerHelper.setCarFloatProperty(AdaptWrapperHelper.functionIdToPropertyId(functionId), zone, functionValue);
    }

    @Override
    public float getFunctionFloatValue(int functionId) {
        return getFunctionFloatValue(functionId, AospPropertyMapping.DEFAULT_ZONE);
    }

    @Override
    public float getFunctionFloatValue(int functionId, int zone) {
        final CarPropertyValue<Float> value = AospPropertyManagerHelper.getCarPropertyValue(AdaptWrapperHelper.functionIdToPropertyId(functionId), zone);
        if (value == null) {
            return IllegalData.FunctionIllegalValue.NOT_AVAILABLE_RTN_VALUE;
        }
        Float valueF = value.getValue();
        return valueF == null ? IllegalData.FunctionIllegalValue.NULL_VALUE : valueF;
    }

    @Override
    public boolean setFunctionBooleanValue(int functionId, boolean functionValue) {
        return setFunctionBooleanValue(functionId, AospPropertyMapping.DEFAULT_ZONE, functionValue);
    }

    @Override
    public boolean setFunctionBooleanValue(int functionId, int zone, boolean functionValue) {
        return AospPropertyManagerHelper.setCarBooleanProperty(AdaptWrapperHelper.functionIdToPropertyId(functionId), zone, functionValue);
    }

    @Override
    public boolean getFunctionBooleanValue(int functionId) {
        return getFunctionBooleanValue(functionId, AospPropertyMapping.DEFAULT_ZONE);
    }

    @Override
    public boolean getFunctionBooleanValue(int functionId, int zone) {
        final CarPropertyValue<Boolean> value = AospPropertyManagerHelper.getCarPropertyValue(AdaptWrapperHelper.functionIdToPropertyId(functionId), zone);
        if (value == null) {
            return false;
        }
        return value.getValue();
    }

    @Override
    public boolean registerFunctionValueWatcher(int functionId, IFunctionValueWatcher watcher) {
        return registerFunctionValueWatcher(new int[]{functionId}, watcher);
    }

    @Override
    public boolean registerFunctionValueWatcher(int[] functionIds, IFunctionValueWatcher watcher) {
        if (functionIds == null || watcher == null) {
            return false;
        }
        final int watcherHashCode = watcher.hashCode();
        PropertyEventCallbackWrapper wrapper = mEventCallbackWrapperMap.get(watcherHashCode);
        if (wrapper == null) {
            wrapper = new PropertyEventCallbackWrapper(watcher);
        }

        ArrayList<Integer> registerSuccessFunctionIds = new ArrayList<>();
        for (int functionId : functionIds) {
            int propId = AdaptWrapperHelper.functionIdToPropertyId(functionId);
            if (!AospPropertyManagerHelper.isCarPropertySupported(propId)) {
                LogUtils.w(TAG, "registerFunctionValueWatcher functionId:" + functionId + ", but not supported!");
                watcher.onFunctionSupportedChanged(functionId, AospPropertyMapping.DEFAULT_ZONE, FunctionStatus.STATUS_NOT_AVAILABLE);
            } else if (!isFunctionActive(functionId)) {
                LogUtils.w(TAG, "registerFunctionValueWatcher functionId:" + functionId + ", but not active!");
                watcher.onFunctionSupportedChanged(functionId, AospPropertyMapping.DEFAULT_ZONE, FunctionStatus.STATUS_NOT_ACTIVE);
            } else {
                boolean registerSuccess = AospPropertyManagerHelper.registerPropertyEventCallback(wrapper, propId);
                if (registerSuccess) {
                    registerSuccessFunctionIds.add(functionId);
                    watcher.onFunctionSupportedChanged(functionId, AospPropertyMapping.DEFAULT_ZONE, FunctionStatus.STATUS_ACTIVE);
                } else {
                    LogUtils.w(TAG, "registerFunctionValueWatcher functionId:" + functionId + ", but error!");
                    watcher.onFunctionSupportedChanged(functionId, AospPropertyMapping.DEFAULT_ZONE, FunctionStatus.STATUS_ERR);
                }
            }
            wrapper.addRegisteredId(functionId, propId);
        }
        mEventCallbackWrapperMap.put(watcherHashCode, wrapper);
        LogUtils.d(TAG, "registerFunctionValueWatcher success, functionIds:" + registerSuccessFunctionIds);
        return true;
    }

    @Override
    public boolean unregisterFunctionValueWatcher(IFunctionValueWatcher watcher) {
        if (watcher == null) {
            return false;
        }
        final PropertyEventCallbackWrapper wrapper = mEventCallbackWrapperMap.remove(watcher.hashCode());
        if (wrapper == null) {
            LogUtils.w(TAG, "unregisterFunctionValueWatcher watcher:" + watcher + ", but not registered.");
            return false;
        }
        boolean result = unregisterPropertyCallback(wrapper);
        LogUtils.d(TAG, "unregisterFunctionValueWatcher success, watcher:" + watcher);
        return result;
    }

    private boolean unregisterPropertyCallback(PropertyEventCallbackWrapper callbackWrapper) {
        if (callbackWrapper != null) {
            boolean result = AospPropertyManagerHelper.unregisterPropertyEventCallback(callbackWrapper);
            if (result) {
                callbackWrapper.clear();
            }
            return result;
        }
        return false;
    }

}
